package org.fjsei.yewu.config;

import org.fjsei.yewu.security.JwtAuthenticationEntryPoint;
import org.fjsei.yewu.security.JwtSecurityConfig;
import org.fjsei.yewu.security.JwtTokenProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.filter.CorsFilter;

/*
只有加了@EnableGlobalMethodSecurity(prePostEnabled=true) 那么在上面使用的 @PreAuthorize(“hasAuthority(‘admin’)”)才会生效
前置注解[@PreAuthorize,@PostAuthorize,..] 是否启用;    https://blog.csdn.net/ywb201314/article/details/127229807
* */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
    private final JwtTokenProvider jwtTokenProvider;
    private final CorsFilter corsFilter;
    private JwtAuthenticationEntryPoint unauthorizedHandler;
    @Value("${sei.BCrypt.strength:11}")
    private int bCryptStrength;

    public SecurityConfig(JwtAuthenticationEntryPoint unauthorizedHandler,
                          JwtTokenProvider jwtTokenProvider,
                          CorsFilter corsFilter
    ) {
        this.unauthorizedHandler = unauthorizedHandler;
        this.jwtTokenProvider = jwtTokenProvider;
        this.corsFilter = corsFilter;
    }
    //第二步：
    @Bean
    public PasswordEncoder passwordEncoder() {
        //随机盐參數也在密文里面,只能支持72个字符密码长度！速度很慢，故意的？1秒验证; https://www.kancloud.cn/liquanqiang/spring-security/757177
        //参数修改后，数据库遗留Hash密码不需要重新生成，旧Hash照样能够验证匹配的！前端js里面有同样功能bcrypt库，也是从DB数据库读取hash来核对验证的。
        PasswordEncoder encoder=new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2Y, bCryptStrength);
        //慢哈希是指执行这个哈希函数非常慢! 实际电脑性能调整。Salt防彩虹表攻击，自适应功能，增加迭代计数以使其执行更慢，在增加计算能力仍然能保持抵抗暴力攻击。
        //参数strength调节对性能服务器CPU影响：strength取值20耗时242s,取15耗时8s,取14耗时4s,取13耗时2s,而strength缺省=10的;看实际生产环境承受能力确定strength。
        //strength亦称为LOG ROUND参数是做加密时重复处理的轮数，且复杂度是指数级递增，默认10的是2的10次方; 实际生成hash长度大约23字节的空间=184位。
        return encoder;
    }


/** 一般的前后端分离项目中，token展现出了它的优势，成为了比session更好的选择;在多个服务器上部署需要将session保存到数据库;
 if not configed URI:throw to web 404: "Whitelabel" Error Page"
 安全配置说明文档：     https://blog.csdn.net/qq_44709990/article/details/123082560
 动态权限拦截器自定义一个Filter去完成权限判断  https://baijiahao.baidu.com/s?id=1714771352683291467&wfr=spider&for=pc
 所以需要用 SessionCreationPolicy.STATELESS 无状态;
 SSO单点登录：用户角色ROLE_的注入？最好直接从后端服务器本地数据库用户表中读取角色配置的方式更安全些，而不是由外部中心授权管理器注入角色名字让后端采信的模式。
* */
//旧版本的    @Bean
//    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//        http.csrf(Customizer.withDefaults())         //.disable()
//            .exceptionHandling(Customizer.withDefaults())
////          .authenticationEntryPoint(unauthorizedHandler)       .and()
////          .formLogin(Customizer.withDefaults())    .disable()
//            .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
//            .sessionManagement(Customizer.withDefaults())
////          .sessionCreationPolicy(SessionCreationPolicy.STATELESS)     .and()
//            .authorizeHttpRequests((authorizeHttpRequests) ->
//                        authorizeHttpRequests
//                                .requestMatchers(AntPathRequestMatcher.antMatcher("/graphql")).permitAll()
//                                .requestMatchers(AntPathRequestMatcher.antMatcher("/**")).permitAll()
//                                .requestMatchers(AntPathRequestMatcher.antMatcher("/test/**")).hasRole("USER")
//                )
////            .antMatchers("/graphql") .permitAll() use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).
////            .antMatchers("/subscriptions").permitAll()
////            .antMatchers("/services/reindexHsEs").access("hasAnyRole('Master')")
// //.requestMatchers("/**").hasRole("USER")
////            .antMatchers("/unitID")              .access("hasAnyRole('Master')")
////                .antMatchers("/api/{departStatus}/{userNickname}/reportUpload").access("hasAnyRole('USER','ADMIN')")
////                .antMatchers("/api/friend/friendMatch/{postFriendNickname}").access("hasAnyRole('USER','ADMIN')")
//      //      .anyRequest()
//       //         .authenticated()
//     //           .and()
//            .headers(Customizer.withDefaults())
////          .frameOptions()        .sameOrigin()    //required to set for H2 else H2 Console will be blank.
////          .cacheControl()        .disable()    .and()
//            .apply(new JwtSecurityConfig(jwtTokenProvider));
//
//        return http.build();
//    }


    /**boot3版本： 基于 Lambda 的配置来代替传统的链式配置，   http://lihuaxi.xjx100.cn/news/1245210.html
     * Spring Security在6.0弃用WebSecurityConfigurationAdapter后该如何自定义配置  https://blog.csdn.net/weixin_59216829/article/details/130098599
     * .hasRole("USER") .hasAuthority("USER") 就差前缀"ROLE_**";
     * */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
            .authorizeHttpRequests(auth -> auth
                    .requestMatchers(AntPathRequestMatcher.antMatcher("/graphql")).permitAll()
                    .requestMatchers(AntPathRequestMatcher.antMatcher("/**")).permitAll()
                    .requestMatchers(AntPathRequestMatcher.antMatcher("/test/**")).hasRole("USER")
                    //.antMatchers("/subscriptions").permitAll() .antMatchers("/services/reindexHsEs").access("hasAnyRole('Master')")
                    //.antMatchers("/api/{departStatus}/{userNickname}/reportUpload").access("hasAnyRole('USER','ADMIN')")
                    .anyRequest().authenticated()
            )
//            .formLogin(form -> form.loginProcessingUrl("/login").usernameParameter("name").passwordParameter("passwd") )
            .csrf(AbstractHttpConfigurer::disable)
            .sessionManagement(session -> session
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .maximumSessions(1).maxSessionsPreventsLogin(true)
            )
            //.cacheControl().disable() .and()
            .apply(new JwtSecurityConfig(jwtTokenProvider));
        return http.build();
    }

}

